"""
IP地址和网络速度监控工具 - OOP重构版本

功能：
1. 获取公网IP地址和地理位置
2. 实时显示网络上传/下载速度
3. 可拖动的桌面悬浮窗显示
"""

import requests
import json
import tkinter as tk
from tkinter import font as tkFont
import psutil
import time
from typing import Optional, Tuple
from dataclasses import dataclass


@dataclass
class Config:
    """配置管理类 - 集中管理所有配置常量"""
    
    # API配置
    IP_API_URL: str = "http://ip-api.com/json/"
    API_TIMEOUT: int = 10  # 秒
    
    # 窗口配置
    WINDOW_MARGIN: int = 10  # 窗口边距
    WINDOW_PADDING_X: int = 10  # 内边距X
    WINDOW_PADDING_Y: int = 5   # 内边距Y
    
    # 外观配置
    BG_COLOR: str = "#333333"  # 深色背景
    FG_COLOR: str = "red"      # 红色文字
    FONT_FAMILY: str = "Consolas"  # 等宽字体
    FONT_SIZE: int = 10
    
    # 更新配置
    UPDATE_INTERVAL_MS: int = 1000  # 更新间隔(毫秒)
    
    # 单位转换常量
    BYTES_PER_KB: int = 1024
    BYTES_PER_MB: int = 1024 * 1024


class IPLocationService:
    """IP地址定位服务类"""
    
    def __init__(self, config: Config):
        """
        初始化IP定位服务
        
        Args:
            config: 配置对象
        """
        self.config = config
    
    def get_location(self) -> str:
        """
        获取公网IP地址及其地理位置信息
        
        Returns:
            格式化的IP和位置信息字符串，或错误信息
        """
        try:
            response = requests.get(
                self.config.IP_API_URL, 
                timeout=self.config.API_TIMEOUT
            )
            response.raise_for_status()  # 检查HTTP错误
            
            try:
                data = response.json()
                
                # 检查API返回状态
                if data.get('status') == 'success':
                    ip_address = data.get('query', 'N/A')
                    city = data.get('city', 'N/A')
                    country = data.get('country', 'N/A')
                    return f"IP: {ip_address}\nCity: {city}\nCountry: {country}"
                else:
                    error_message = data.get('message', '未知API错误')
                    return f"API错误: {error_message}"
            
            except json.JSONDecodeError:
                return "错误: 无法解析API响应"
            except Exception as e:
                return f"错误: 数据处理失败 - {e}"
        
        except requests.exceptions.Timeout:
            return "错误: 请求超时"
        except requests.exceptions.ConnectionError:
            return "错误: 连接失败"
        except requests.exceptions.HTTPError as e:
            return f"错误: HTTP错误 - {e}"
        except requests.exceptions.RequestException as e:
            return f"错误: 请求失败 - {e}"


class NetworkSpeedTracker:
    """网络速度追踪器类"""
    
    def __init__(self, config: Config):
        """
        初始化网络速度追踪器
        
        Args:
            config: 配置对象
        """
        self.config = config
        self.last_time: Optional[float] = None
        self.last_sent: Optional[int] = None
        self.last_recv: Optional[int] = None
        self.upload_speed: float = 0.0
        self.download_speed: float = 0.0
    
    def update(self) -> bool:
        """
        更新网络速度数据
        
        Returns:
            True表示成功更新，False表示首次运行(无历史数据)
        """
        try:
            current_time = time.time()
            current_counters = psutil.net_io_counters()
            current_sent = current_counters.bytes_sent
            current_recv = current_counters.bytes_recv
            
            # 首次运行，只记录数据
            if self.last_time is None:
                self.last_time = current_time
                self.last_sent = current_sent
                self.last_recv = current_recv
                return False
            
            # 计算时间和流量差值
            time_delta = current_time - self.last_time
            sent_delta = current_sent - self.last_sent
            recv_delta = current_recv - self.last_recv
            
            # 计算速度 (字节/秒)
            if time_delta > 0:
                self.upload_speed = sent_delta / time_delta
                self.download_speed = recv_delta / time_delta
            else:
                self.upload_speed = 0.0
                self.download_speed = 0.0
            
            # 更新历史数据
            self.last_time = current_time
            self.last_sent = current_sent
            self.last_recv = current_recv
            
            return True
            
        except Exception as e:
            # 发生错误时返回False
            print(f"网络速度更新错误: {e}")
            return False
    
    def get_upload_speed(self) -> float:
        """获取上传速度(字节/秒)"""
        return self.upload_speed
    
    def get_download_speed(self) -> float:
        """获取下载速度(字节/秒)"""
        return self.download_speed
    
    def format_speed(self, speed_bytes_sec: float) -> str:
        """
        格式化速度显示
        
        Args:
            speed_bytes_sec: 速度值(字节/秒)
        
        Returns:
            格式化的速度字符串 (如: "1.2 MB/s")
        """
        if speed_bytes_sec < self.config.BYTES_PER_KB:
            return f"{speed_bytes_sec:.1f} B/s"
        elif speed_bytes_sec < self.config.BYTES_PER_MB:
            return f"{speed_bytes_sec / self.config.BYTES_PER_KB:.1f} KB/s"
        else:
            return f"{speed_bytes_sec / self.config.BYTES_PER_MB:.1f} MB/s"


class OverlayWindow:
    """桌面悬浮窗口类"""
    
    def __init__(self, ip_info: str, config: Config):
        """
        初始化悬浮窗口
        
        Args:
            ip_info: 要显示的IP信息
            config: 配置对象
        """
        self.ip_info = ip_info
        self.config = config
        self.speed_tracker = NetworkSpeedTracker(config)
        
        # 初始化Tkinter组件
        self.root = tk.Tk()
        self.root.withdraw()  # 隐藏主窗口
        
        self.overlay = tk.Toplevel(self.root)
        self.overlay.overrideredirect(True)  # 移除窗口装饰
        self.overlay.wm_attributes("-topmost", 1)  # 置顶显示
        
        # 拖动相关的起始坐标
        self.drag_start_x: int = 0
        self.drag_start_y: int = 0
        
        self.label: Optional[tk.Label] = None
    
    def setup_ui(self) -> None:
        """设置UI界面元素"""
        # 创建字体
        label_font = tkFont.Font(
            family=self.config.FONT_FAMILY,
            size=self.config.FONT_SIZE
        )
        
        # 初始显示文本
        initial_text = f"{self.ip_info}\nUp: 计算中...\nDown: 计算中..."
        
        # 创建标签
        self.label = tk.Label(
            self.overlay,
            text=initial_text,
            font=label_font,
            bg=self.config.BG_COLOR,
            fg=self.config.FG_COLOR,
            justify=tk.LEFT,
            padx=self.config.WINDOW_PADDING_X,
            pady=self.config.WINDOW_PADDING_Y
        )
        self.label.pack()
        
        # 绑定事件
        self._setup_events()
    
    def _setup_events(self) -> None:
        """设置事件绑定(拖动和关闭)"""
        # 绑定拖动事件到窗口和标签
        self.overlay.bind("<ButtonPress-1>", self._on_drag_start)
        self.overlay.bind("<B1-Motion>", self._on_drag_motion)
        self.label.bind("<ButtonPress-1>", self._on_drag_start)
        self.label.bind("<B1-Motion>", self._on_drag_motion)
        
        # 绑定右键关闭事件
        self.overlay.bind("<Button-3>", lambda e: self.root.destroy())
        self.label.bind("<Button-3>", lambda e: self.root.destroy())
    
    def _on_drag_start(self, event: tk.Event) -> None:
        """
        拖动开始事件处理
        
        Args:
            event: 鼠标事件对象
        """
        self.drag_start_x = event.x
        self.drag_start_y = event.y
    
    def _on_drag_motion(self, event: tk.Event) -> None:
        """
        拖动移动事件处理
        
        Args:
            event: 鼠标事件对象
        """
        new_x = self.overlay.winfo_x() + (event.x - self.drag_start_x)
        new_y = self.overlay.winfo_y() + (event.y - self.drag_start_y)
        self.overlay.geometry(f"+{new_x}+{new_y}")
    
    def position_window(self) -> None:
        """将窗口定位到屏幕右上角"""
        # 更新窗口以获取实际尺寸
        self.overlay.update_idletasks()
        
        # 获取屏幕宽度和窗口宽度
        screen_width = self.overlay.winfo_screenwidth()
        window_width = self.overlay.winfo_reqwidth()
        
        # 计算右上角位置
        x_pos = screen_width - window_width - self.config.WINDOW_MARGIN
        y_pos = self.config.WINDOW_MARGIN
        
        # 设置窗口位置
        self.overlay.geometry(f"+{x_pos}+{y_pos}")
    
    def update_display(self) -> None:
        """更新显示内容(定期调用)"""
        try:
            # 更新网络速度数据
            has_data = self.speed_tracker.update()
            
            if has_data:
                # 获取并格式化速度
                upload = self.speed_tracker.get_upload_speed()
                download = self.speed_tracker.get_download_speed()
                up_str = self.speed_tracker.format_speed(upload)
                down_str = self.speed_tracker.format_speed(download)
                new_text = f"{self.ip_info}\nUp: {up_str}\nDown: {down_str}"
            else:
                # 首次运行，显示计算中
                new_text = f"{self.ip_info}\nUp: 计算中...\nDown: 计算中..."
            
            # 更新标签文本
            if self.label:
                self.label.config(text=new_text)
        
        except Exception as e:
            # 错误处理
            if self.label:
                self.label.config(
                    text=f"{self.ip_info}\n错误: 更新速度失败\n{e}"
                )
        
        finally:
            # 安排下次更新
            self.overlay.after(
                self.config.UPDATE_INTERVAL_MS,
                self.update_display
            )
    
    def run(self) -> None:
        """启动窗口主循环"""
        # 设置UI
        self.setup_ui()
        
        # 定位窗口
        self.position_window()
        
        # 启动第一次更新
        self.overlay.after(
            self.config.UPDATE_INTERVAL_MS,
            self.update_display
        )
        
        # 进入主循环
        self.root.mainloop()


def main() -> None:
    """主函数 - 程序入口"""
    # 创建配置
    config = Config()
    
    # 获取IP信息
    ip_service = IPLocationService(config)
    ip_info = ip_service.get_location()
    
    # 创建并运行悬浮窗口
    window = OverlayWindow(ip_info, config)
    window.run()


if __name__ == "__main__":
    main()